package com.kpmg.datalake.schedule.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.kpmg.datalake.schedule.enums.ScheduleStatusEnum;
import com.kpmg.datalake.schedule.enums.YesOrNoEnum;
import com.kpmg.datalake.schedule.model.ScheduleJob;
import com.kpmg.datalake.schedule.service.IScheduleJobService;
import com.kpmg.datalake.schedule.service.IScheduleMangerService;
import com.kpmg.datalake.schedule.utils.CronDateUtils;
import com.kpmg.datalake.schedule.utils.ServerIpUtil;
import com.kpmg.datalake.schedule.utils.TaskUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

/**
 * @author Alex.zhao [alex.zhao@kpmg.com]
 * @since Created on azhao6 on 2018-08-09 21:34:26 任务管理服务
 */
@Service
public class ScheduleMangerServiceImpl implements IScheduleMangerService, ApplicationContextAware,
    ApplicationListener<ContextRefreshedEvent> {

  private static final Logger logger = LoggerFactory.getLogger(ScheduleMangerServiceImpl.class);

  private static ApplicationContext context = null;
  @Resource
  private SchedulerFactoryBean schedulerFactoryBean;
  @Resource
  private IScheduleJobService scheduleJobService;

  /**
   * 根据类从spring上下文获取bean。
   *
   * @param cls cls
   * @return T
   */
  public static <T> T getBean(Class<T> cls) {
    return context.getBean(cls);
  }

  /**
   * 根据类从spring上下文获取bean。
   *
   * @param beanId cls
   * @return Object
   */
  public static Object getBean(String beanId) {
    return context.getBean(beanId);
  }

  /**
   * 添加任务
   *
   * @param job job
   * @throws SchedulerException SchedulerException
   */
  @Override
  public void addJob(ScheduleJob job) throws SchedulerException {
    if (job == null || !ScheduleStatusEnum.RUNNING.getCode().equals(job.getJobStatus())) {
      return;
    }
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
    //如果执行周期为空 表示5秒立即执行
    if (StringUtils.isBlank(job.getJobCron())) {
      job.setJobCron(CronDateUtils.getNextFiveTime());
    }
    // 不存在，创建一个
    if (null == trigger) {
      Class<? extends Job> clazz =
          YesOrNoEnum.YES.getCode().equals(job.getJobConcurrent()) ? QuartzJobFactory.class
              : QuartzJobFactoryDisallowConcurrentExecution.class;
      JobDetail jobDetail = JobBuilder.newJob(clazz)
          .withIdentity(job.getJobName(), job.getJobGroup()).build();
      jobDetail.getJobDataMap().put(TaskUtils.SCHEDULE_JOB, job);
      CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getJobCron());
      trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
          .withSchedule(scheduleBuilder).build();
      scheduler.scheduleJob(jobDetail, trigger);
      // Trigger已存在，那么更新相应的定时设置
    } else {
      JobDetail jobDetail = scheduler.getJobDetail(trigger.getJobKey());
      jobDetail.getJobDataMap().put(TaskUtils.SCHEDULE_JOB, job);
      CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getJobCron());
      // 按新的cronExpression表达式重新构建trigger
      trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder)
          .build();
      // 按新的trigger重新设置job执行
      scheduler.addJob(jobDetail, true, true);
      scheduler.rescheduleJob(triggerKey, trigger);
    }
  }


  /**
   * 获取所有计划中的任务列表
   *
   * @return List
   * @throws SchedulerException SchedulerException
   */
  @Override
  public List<ScheduleJob> getAllJob() throws SchedulerException {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
    Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
    List<ScheduleJob> jobList = new ArrayList<>();
    for (JobKey jobKey : jobKeys) {
      List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
      for (Trigger trigger : triggers) {
        ScheduleJob job = new ScheduleJob();
        job.setJobName(jobKey.getName());
        job.setJobGroup(jobKey.getGroup());
        job.setJobDesc("触发器:" + trigger.getKey());
        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
        job.setJobStatus(triggerState.name());
        if (trigger instanceof CronTrigger) {
          CronTrigger cronTrigger = (CronTrigger) trigger;
          String cronExpression = cronTrigger.getCronExpression();
          job.setJobCron(cronExpression);
        }
        jobList.add(job);
      }
    }
    return jobList;
  }

  /**
   * 所有正在运行的job
   *
   * @return List
   * @throws SchedulerException SchedulerException
   */
  @Override
  public List<ScheduleJob> getRunningJob() throws SchedulerException {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
    List<ScheduleJob> jobList = new ArrayList<>(executingJobs.size());
    for (JobExecutionContext executingJob : executingJobs) {
      ScheduleJob job = new ScheduleJob();
      JobDetail jobDetail = executingJob.getJobDetail();
      JobKey jobKey = jobDetail.getKey();
      Trigger trigger = executingJob.getTrigger();
      job.setJobName(jobKey.getName());
      job.setJobGroup(jobKey.getGroup());
      job.setJobDesc("触发器:" + trigger.getKey());
      Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
      job.setJobStatus(triggerState.name());
      if (trigger instanceof CronTrigger) {
        CronTrigger cronTrigger = (CronTrigger) trigger;
        String cronExpression = cronTrigger.getCronExpression();
        job.setJobCron(cronExpression);
      }
      jobList.add(job);
    }
    return jobList;
  }

  /**
   * 暂停一个job
   *
   * @param scheduleJob scheduleJob
   * @throws SchedulerException SchedulerException
   */
  @Override
  public void pauseJob(ScheduleJob scheduleJob) throws SchedulerException {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    scheduler.pauseJob(jobKey);
  }

  /**
   * 恢复一个job
   *
   * @param scheduleJob scheduleJob
   * @throws SchedulerException SchedulerException
   */
  @Override
  public void resumeJob(ScheduleJob scheduleJob) throws SchedulerException {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    scheduler.resumeJob(jobKey);
  }

  /**
   * 删除一个job
   *
   * @param scheduleJob scheduleJob
   * @throws SchedulerException SchedulerException
   */
  @Override
  public void deleteJob(ScheduleJob scheduleJob) throws SchedulerException {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    scheduler.deleteJob(jobKey);

  }

  /**
   * 立即执行job
   *
   * @param scheduleJob scheduleJob
   * @throws SchedulerException SchedulerException
   */
  @Override
  public void runAJobNow(ScheduleJob scheduleJob) throws SchedulerException {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    scheduler.triggerJob(jobKey);
  }

  /**
   * 更新job时间表达式
   *
   * @param scheduleJob scheduleJob
   * @throws SchedulerException SchedulerException
   */
  @Override
  public void updateJobCron(ScheduleJob scheduleJob) throws SchedulerException {
    Scheduler scheduler = schedulerFactoryBean.getScheduler();
    TriggerKey triggerKey = TriggerKey
        .triggerKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder
        .cronSchedule(scheduleJob.getJobCron());
    trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder)
        .build();
    scheduler.rescheduleJob(triggerKey, trigger);
  }

  /**
   * 验证bean是否合法
   *
   * @param instanceName instanceName
   * @return Boolean
   */
  @Override
  public Boolean validateBeanOrClass(String instanceName) {
    boolean result = true;
    try {
      Object bean = context.getBean(instanceName);
      if (bean == null) {
        Class clazz = Class.forName(instanceName);
        clazz.newInstance();
      }
    } catch (Exception e) {
      result = false;
      logger.error(e.getMessage(), e);
    }
    return result;
  }


  @Override
  public synchronized void setApplicationContext(ApplicationContext applicationContext) {
    context = applicationContext;
  }

  /**
   * 服务启动增加 任务监听构造器
   *
   * @param event 上下问刷新方法
   */
  @Override
  public void onApplicationEvent(ContextRefreshedEvent event) {
    if (event.getApplicationContext().getParent() == null) {
      String allowIp = scheduleJobService.selectScheduleAllowIp();
      allowIp = StringUtils.isNotBlank(allowIp) ? allowIp : "";
      List<String> serverIp = ServerIpUtil.getServerIp();
      serverIp.retainAll(Arrays.asList(allowIp.split(",")));
      //allowIp为空时表示不限制IP
      if (StringUtils.isBlank(allowIp) || !CollectionUtils.isEmpty(serverIp)) {
        ScheduleJob scheduleJob = new ScheduleJob();
        scheduleJob.setJobStatus(ScheduleStatusEnum.RUNNING.getCode());
        scheduleJob.setDelInd(YesOrNoEnum.NO.getIntCode());
        List<ScheduleJob> jobList = scheduleJobService.selectList(new EntityWrapper<>(scheduleJob));
        // 这里获取任务信息数据
        for (ScheduleJob job : jobList) {
          try {
            this.addJob(job);
          } catch (Exception e) {
            logger.error(e.getMessage());
          }
        }
      }
    }
  }
}
